package com.version1.diabeco.data.mapas;

import java.util.ArrayList;
import java.util.List;

import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;

import com.version1.diabeco.data.AbstractDao;
import com.version1.diabeco.data.DiabecoContract;
import com.version1.diabeco.data.comida.ComidaDaoImpl;
import com.version1.diabeco.model.mapas.Geocerca;
import com.version1.diabeco.model.mapas.VerticeGeocerca;

public final class GeocercaDaoImpl extends AbstractDao implements GeocercaDao {

	private Context context;

	private static GeocercaDaoImpl instance;

	private GeocercaDaoImpl(Context context) {
		super(context);
	}

	/**
	 * Retrieves the singleton instance. Receives a context which is used for database initialization but also stored
	 * for subsequent sql operation calls.
	 * 
	 * This method is thread-safe.
	 * 
	 * @return AlimentoDao with the instance.
	 */
	public static synchronized GeocercaDaoImpl getInstance(Context context) {
		if (instance == null) {
			instance = new GeocercaDaoImpl(context);
		}

		instance.setContext(context);

		return instance;
	}

	public Context getContext() {
		return context;
	}

	public void setContext(Context context) {
		this.context = context;
	}

	@Override
	public Long count() {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Geocerca selectById(Long id) {

		Geocerca result = null;

		SQLiteDatabase db = GeocercaDaoImpl.getInstance(this.getContext()).getReadableDatabase();

		List<VerticeGeocerca> vertices = this.loadVerticesGeocerca(db, id);

		String[] projection = { DiabecoContract.Geocerca._ID, DiabecoContract.Geocerca.COLUMNA_NOMBRE,
				DiabecoContract.Geocerca.COLUMNA_TIPO_REGISTRO, DiabecoContract.Geocerca.COLUMNA_ALIAS,
				DiabecoContract.Geocerca.COLUMNA_ACTIVADA, DiabecoContract.Geocerca.COLUMNA_ULTIMO_INGRESO, 
				DiabecoContract.Geocerca.COLUMNA_ULTIMO_EGRESO };

		String selection = DiabecoContract.RegistroComida._ID + " = ?";
		String[] selectionArgs = { id.toString() };
		String sortOrder = DiabecoContract.RegistroComida.COLUMNA_FECHA + " DESC";

		Cursor cursor = db.query(DiabecoContract.RegistroComida.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				sortOrder // The sort order
				);

		cursor.moveToFirst();
		result = this.geocercaToDomainObjectList(cursor);
		result.setVertices(vertices);

		return result;

	}

	private List<VerticeGeocerca> loadVerticesGeocerca(SQLiteDatabase db, Long id) {
		List<VerticeGeocerca> result = new ArrayList<VerticeGeocerca>();

		String[] projection = { DiabecoContract.VerticeGeocerca._ID,
				DiabecoContract.VerticeGeocerca.COLUMNA_ID_GEOCERCA, DiabecoContract.VerticeGeocerca.COLUMNA_ORDEN,
				DiabecoContract.VerticeGeocerca.COLUMNA_LATITUD, DiabecoContract.VerticeGeocerca.COLUMNA_LONGITUD };
		String selection = DiabecoContract.VerticeGeocerca.COLUMNA_ID_GEOCERCA + " = ?";
		String[] selectionArgs = { id.toString() };

		Cursor cursor = db.query(DiabecoContract.VerticeGeocerca.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				null // The sort order
				);

		while (cursor.moveToNext()) {
			VerticeGeocerca verticeGeocerca = this.verticeGeocercaToDomainObjectList(cursor);
			result.add(verticeGeocerca);
		}
		return result;
	}

	@Override
	public List<Geocerca> selectAll() {

		List<Geocerca> result = new ArrayList<Geocerca>();

		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getReadableDatabase();

		// Define a projection that specifies which columns from the database
		// you will actually use after this query.
		String[] projection = { DiabecoContract.Geocerca._ID, DiabecoContract.Geocerca.COLUMNA_NOMBRE,
				DiabecoContract.Geocerca.COLUMNA_TIPO_REGISTRO, DiabecoContract.Geocerca.COLUMNA_ALIAS,
				DiabecoContract.Geocerca.COLUMNA_ACTIVADA, DiabecoContract.Geocerca.COLUMNA_ULTIMO_INGRESO, 
				DiabecoContract.Geocerca.COLUMNA_ULTIMO_EGRESO };

		String selection = null;
		String[] selectionArgs = null;

		Cursor cursor = db.query(DiabecoContract.Geocerca.TABLE_NAME, // The table to query
				projection, // The columns to return
				selection, // The columns for the WHERE clause
				selectionArgs, // The values for the WHERE clause
				null, // don't group the rows
				null, // don't filter by row groups
				null // The sort order
				);

		while (cursor.moveToNext()) {
			Geocerca geocerca = this.geocercaToDomainObjectList(cursor);

			// TODO: evaluar performance por la cantidad de queries.
			List<VerticeGeocerca> verticeGeocerca = this.loadVerticesGeocerca(db, geocerca.getId());

			geocerca.setVertices(verticeGeocerca);
			result.add(geocerca);
		}

		return result;
	}

	public void save(Geocerca geocerca) {

		// Gets the data repository in write mode
		SQLiteDatabase db = GeocercaDaoImpl.getInstance(this.getContext()).getWritableDatabase();
		db.beginTransaction();

		try {

			long rowsAffected = 0;
			long geocercaId;

			ContentValues values = new ContentValues();
			values.put(DiabecoContract.Geocerca.COLUMNA_NOMBRE, geocerca.getNombre());
			values.put(DiabecoContract.Geocerca.COLUMNA_ALIAS, geocerca.getAlias());
			values.put(DiabecoContract.Geocerca.COLUMNA_TIPO_REGISTRO, geocerca.getTipoRegistro());
			values.put(DiabecoContract.Geocerca.COLUMNA_ACTIVADA, geocerca.isActivada());
			values.put(DiabecoContract.Geocerca.COLUMNA_ULTIMO_INGRESO, geocerca.getUltimoIngreso());
			values.put(DiabecoContract.Geocerca.COLUMNA_ULTIMO_EGRESO, geocerca.getUltimoEgreso());

			if (geocerca.getId() != null) {
				geocercaId = geocerca.getId();

				// borro la geocerca asi se borran los vertices
				db.execSQL("PRAGMA foreign_keys = ON;");
				this.doDeleteById(db, geocercaId);

				// rowsAffected = db.update(DiabecoContract.RegistroComida.TABLE_NAME, values,
				// DiabecoContract.RegistroComida._ID + " = ?", new String[] { comida.getId().toString() });
				// System.out.println("cantidad comida actualizados: " + rowsAffected);

			}
			// Insert the new row, returning the primary key value of the new row
			geocercaId = db.insert(DiabecoContract.Geocerca.TABLE_NAME, null, values);
			System.out.println("geocercaId id: " + geocercaId);

			for (VerticeGeocerca vertice : geocerca.getVertices()) {
				ContentValues verticeGeocercaValues = new ContentValues();
				verticeGeocercaValues.put(DiabecoContract.VerticeGeocerca.COLUMNA_ID_GEOCERCA, geocercaId);
				verticeGeocercaValues.put(DiabecoContract.VerticeGeocerca.COLUMNA_ORDEN, vertice.getOrden());
				verticeGeocercaValues.put(DiabecoContract.VerticeGeocerca.COLUMNA_LATITUD, vertice.getLatitud());
				verticeGeocercaValues.put(DiabecoContract.VerticeGeocerca.COLUMNA_LONGITUD, vertice.getLongitud());

				// por el cascade, todos los vertices se borran
				// Insert the new row, returning the primary key value of the new row
				long verticeGeocercaId = db.insert(DiabecoContract.VerticeGeocerca.TABLE_NAME, null,
						verticeGeocercaValues);
				System.out.println("vertice id: " + verticeGeocercaId);

			}

			db.setTransactionSuccessful();
		} finally {
			db.endTransaction();
		}

	}

	@Override
	public void deleteById(Long comidaId) {

		// Gets the data repository in write mode
		SQLiteDatabase db = ComidaDaoImpl.getInstance(this.getContext()).getWritableDatabase();
		// Activo foreign keys
		db.execSQL("PRAGMA foreign_keys = ON;");

		this.doDeleteById(db, comidaId);
	}

	private void doDeleteById(SQLiteDatabase db, Long geocercaId) {

		String removeId = geocercaId.toString();

		int rowsDeleted = db.delete(DiabecoContract.Geocerca.TABLE_NAME, DiabecoContract.Geocerca._ID + "=?",
				new String[] { removeId });

		if (rowsDeleted != 1) {
			throw new RuntimeException("No se pudo eliminar el registro de geocerca con id " + geocercaId);
		}

	}

	@Override
	public void clear() {
		// TODO Auto-generated method stub

	}

	private VerticeGeocerca verticeGeocercaToDomainObjectList(Cursor cursor) {
		VerticeGeocerca result = new VerticeGeocerca();

		Long id = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.VerticeGeocerca._ID));
		Long orden = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.VerticeGeocerca.COLUMNA_ORDEN));
		Double latitud = cursor.getDouble(cursor.getColumnIndexOrThrow(DiabecoContract.VerticeGeocerca.COLUMNA_LATITUD));
		Double longitud = cursor.getDouble(cursor.getColumnIndexOrThrow(DiabecoContract.VerticeGeocerca.COLUMNA_LONGITUD));

		result.setId(id);
		result.setOrden(orden);
		result.setLatitud(latitud);
		result.setLongitud(longitud);

		System.out.println("Carga vertice geocerca: " + result);

		return result;
	}

	private Geocerca geocercaToDomainObjectList(Cursor cursor) {
		Geocerca geocerca = new Geocerca();

		Long id = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca._ID));
		String alias = cursor.getString(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_ALIAS));
		String nombre = cursor.getString(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_NOMBRE));
		Boolean activada = 1 == cursor.getInt(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_ACTIVADA));
		Long ultimoIngreso = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_ULTIMO_INGRESO));
		Long ultimoEgreso = cursor.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_ULTIMO_EGRESO));
		Long tipoRegistro = cursor
				.getLong(cursor.getColumnIndexOrThrow(DiabecoContract.Geocerca.COLUMNA_TIPO_REGISTRO));

		geocerca.setId(id);
		geocerca.setNombre(nombre);
		geocerca.setAlias(alias);
		geocerca.setActivada(activada);
		geocerca.setUltimoIngreso(ultimoIngreso);
		geocerca.setUltimoEgreso(ultimoEgreso);
		geocerca.setTipoRegistro(tipoRegistro);

		System.out.println("Readed food intake measure: " + geocerca);

		return geocerca;
	}

}
